package service

import (
	"context"
	"encoding/json"
	"fmt"
	"ghw2/server/config"
	"ghw2/server/redis"
	"ghw2/service"
	"ghw2/util"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/keepalive"
	"google.golang.org/grpc/status"
	"math/rand"
	"net"
	"time"
)

type services struct {
	service.UnimplementedBookServiceServer
}

func (services) Add(ctx context.Context, book *service.Book) (*service.AlterResponse, error) {

	fmt.Println(config.ServerConfig.Server, "grpc:Service Book:add is called by remote host.")

	defer func() {
		if err := recover(); err != nil {
			fmt.Println(err)
		}
	}()

	if !util.IsInArray(config.ServerConfig.Services, "Book:add") {
		return nil, status.Errorf(codes.Aborted, "这个方法在这个服务不可取")
	}

	conn := redis.RedisPool.Get()
	defer conn.Close()

	randomInt := time.Now().Unix() + rand.Int63n(114512)

	reply, _ := conn.Do("SET", "locker", string(randomInt), "EX", "10", "NX")

	for reply == nil {
		reply, _ = conn.Do("SET", "locker", "1", "EX", "10", "NX")
	}

	defer conn.Do("evalsha", config.ServerConfig.RedisLuaSha, 1, "locker", string(randomInt))

	searchString := "book:" + fmt.Sprintf("%d", book.ID) + "*"

	if reply, err := conn.Do("keys", searchString); err != nil {
		return nil, status.Errorf(codes.DataLoss, "数据库查询失败！")
	} else {
		if convertReply := reply.([]interface{}); len(convertReply) != 0 {
			return nil, status.Errorf(codes.AlreadyExists, "对应ID数据已经存在")
		}
	}

	name := "book:"
	name += fmt.Sprintf("%d", book.ID)
	name += ":"
	name += book.Name

	data, _ := json.Marshal(*book)
	if _, err := conn.Do("set", name, string(data)); err != nil {
		fmt.Println(err)
		return nil, status.Errorf(codes.Aborted, "这个方法在这个服务不可取")
	}
	return &service.AlterResponse{Ok: true}, nil
}

func (services) QueryByID(ctx context.Context, bookId *service.BookID) (*service.Book, error) {
	fmt.Println(config.ServerConfig.Server, " grpc:Service Book:queryByID is called by remote host.")
	if !util.IsInArray(config.ServerConfig.Services, "Book:queryByID") {
		return nil, status.Errorf(codes.Aborted, "这个方法在这个服务不可取")
	}

	searchString := "book:" + fmt.Sprintf("%d", bookId.ID) + "*"
	fmt.Println("SearchString", searchString)
	conn := redis.RedisPool.Get()
	defer conn.Close()

	if reply, err := conn.Do("keys", searchString); err != nil {
		return nil, status.Errorf(codes.DataLoss, "数据库查询失败！")
	} else {
		if convertReply := reply.([]interface{}); len(convertReply) == 0 {
			return nil, status.Errorf(codes.NotFound, "无对应ID数据")
		} else {
			indexName := string(convertReply[0].([]byte))
			reply, err = conn.Do("get", indexName)
			book := service.Book{}
			json.Unmarshal(reply.([]byte), &book)
			return &book, nil
		}
	}
}

func (services) QueryByName(ctx context.Context, bookName *service.BookName) (*service.BookList, error) {
	fmt.Println(config.ServerConfig.Server, " grpc:Service Book:queryByName is called by remote host.")
	if !util.IsInArray(config.ServerConfig.Services, "Book:queryByName") {
		return nil, status.Errorf(codes.Aborted, "这个方法在这个服务不可取")
	}

	searchString := "book:*" + bookName.Name + "*"

	conn := redis.RedisPool.Get()
	defer conn.Close()

	fmt.Println(searchString)

	if reply, err := conn.Do("keys", searchString); err != nil {
		return nil, status.Errorf(codes.DataLoss, "数据库查询失败！")
	} else {
		fmt.Println(reply)
		if convertReply := reply.([]interface{}); len(convertReply) == 0 {
			return nil, status.Errorf(codes.NotFound, "无对应ID数据")
		} else {
			var bookList service.BookList
			for _, v := range convertReply {
				book := &service.Book{}
				indexName := string(v.([]byte))
				reply, err = conn.Do("get", indexName)
				json.Unmarshal(reply.([]byte), &book)
				bookList.List = append(bookList.List, book)
			}
			return &bookList, nil
		}
	}
}

func (services) Delete(ctx context.Context, bookId *service.BookID) (*service.AlterResponse, error) {
	fmt.Println(config.ServerConfig.Server, " grpc:Service Book:delete is called by remote host.")
	if !util.IsInArray(config.ServerConfig.Services, "Book:delete") {
		return nil, status.Errorf(codes.Aborted, "这个方法在这个服务不可取")
	}

	conn := redis.RedisPool.Get()
	defer conn.Close()

	randomInt := time.Now().Unix() + rand.Int63n(114512)

	ok, _ := conn.Do("SET", "locker", string(randomInt), "EX", "10", "NX")
	for ok != nil {
		ok, _ = conn.Do("SET", "locker", "1", "EX", "10", "NX")
	}

	defer conn.Do("evalsha", config.ServerConfig.RedisLuaSha, 1, "locker", string(randomInt))

	searchString := "book:"
	searchString += fmt.Sprintf("%d", bookId.ID)
	searchString += "*"

	if reply, err := conn.Do("keys", searchString); err != nil {
		return nil, status.Errorf(codes.DataLoss, "数据库查询失败！")
	} else {
		if convertReply := reply.([]interface{}); len(convertReply) == 0 {
			return nil, status.Errorf(codes.NotFound, "无对应ID数据")
		} else {
			indexName := string(convertReply[0].([]byte))
			reply, err = conn.Do("del", indexName)
			if reply.(int64) == 1 {
				return &service.AlterResponse{
					Ok: true,
				}, nil
			} else {
				return &service.AlterResponse{
					Ok: false,
				}, nil
			}
		}
	}

}

func RegisterService(listener net.Listener) error {
	var kaep = keepalive.EnforcementPolicy{
		MinTime:             5 * time.Second, // If a client pings more than once every 5 seconds, terminate the connection
		PermitWithoutStream: true,            // Allow pings even when there are no active streams
	}

	var kasp = keepalive.ServerParameters{
		MaxConnectionIdle:     15 * time.Second, // If a client is idle for 15 seconds, send a GOAWAY
		MaxConnectionAge:      30 * time.Second, // If any connection is alive for more than 30 seconds, send a GOAWAY
		MaxConnectionAgeGrace: 5 * time.Second,  // Allow 5 seconds for pending RPCs to complete before forcibly closing connections
		Time:                  5 * time.Second,  // Ping the client if it is idle for 5 seconds to ensure the connection is still active
		Timeout:               1 * time.Second,  // Wait 1 second for the ping ack before assuming the connection is dead
	}

	grpcServer := grpc.NewServer(grpc.KeepaliveEnforcementPolicy(kaep), grpc.KeepaliveParams(kasp))
	service.RegisterBookServiceServer(grpcServer, &services{})
	if err := grpcServer.Serve(listener); err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}
